package com.atguigu.gmall.product.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.cache.GmallCache;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.model.product.*;
import com.atguigu.gmall.product.mapper.*;
import com.atguigu.gmall.product.service.ManageService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.bytebuddy.asm.Advice;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 后台管理平台中心
 */
@Service
public class ManageServiceImpl implements ManageService {


    @Autowired
    private BaseCategory1Mapper baseCategory1Mapper;
    @Autowired
    private BaseCategory2Mapper baseCategory2Mapper;
    @Autowired
    private BaseCategory3Mapper baseCategory3Mapper;
    @Autowired
    private BaseAttrInfoMapper baseAttrInfoMapper;
    @Autowired
    private BaseAttrValueMapper baseAttrValueMapper;
    @Autowired
    private SpuInfoMapper spuInfoMapper;
    @Autowired
    private BaseSaleAttrMapper baseSaleAttrMapper;
    @Autowired
    private BaseTrademarkMapper baseTrademarkMapper;
    @Autowired
    private SpuImageMapper spuImageMapper;
    @Autowired
    private SpuSaleAttrMapper spuSaleAttrMapper;
    @Autowired
    private SpuSaleAttrValueMapper spuSaleAttrValueMapper;
    @Autowired
    private SkuInfoMapper skuInfoMapper;
    @Autowired
    private SkuImageMapper skuImageMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    //获取一级分类 集合
    @Override
    public List<BaseCategory1> getCategory1() {
        return baseCategory1Mapper.selectList(null);
    }

    //获取二级分类集合
    @Override
    public List<BaseCategory2> getCategory2(Long category1Id) {
        QueryWrapper<BaseCategory2> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("category1_id", category1Id);
        return baseCategory2Mapper.selectList(queryWrapper);
    }

    //获取三级分类集合
    @Override
    public List<BaseCategory3> getCategory3(Long category2Id) {
        QueryWrapper<BaseCategory3> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("category2_id", category2Id);
        return baseCategory3Mapper.selectList(queryWrapper);
    }

    //获取平台属性及属性值集合
    @Override
    public List<BaseAttrInfo> attrInfoList(Long category1Id, Long category2Id, Long category3Id) {
        return baseAttrInfoMapper.attrInfoList(category1Id, category2Id, category3Id);
    }

    //保存
    @Override
    public void saveAttrInfo(BaseAttrInfo baseAttrInfo) {
        //1：平台属性表 1
        baseAttrInfoMapper.insert(baseAttrInfo);
        //2:平台属性值表  多
        List<BaseAttrValue> attrValueList = baseAttrInfo.getAttrValueList();
        attrValueList.forEach(v -> {
            //设置外键
            v.setAttrId(baseAttrInfo.getId());
            baseAttrValueMapper.insert(v);
        });

    }

    //Spu分页查询
    @Override
    public IPage getSpuPage(Integer page, Integer limit, Long category3Id) {
        //page 当前页
        //limit 每页数
        return spuInfoMapper.selectPage(new Page<>(page, limit)
                , new QueryWrapper<SpuInfo>().eq("category3_id", category3Id));
    }

    @Override
    public List<BaseSaleAttr> baseSaleAttrList() {
        return baseSaleAttrMapper.selectList(null);
    }

    @Override
    public List<BaseTrademark> getTrademarkList() {
        return baseTrademarkMapper.selectList(null);
    }


    //保存SPU 数据  四张表的保存
    @Override
    @Transactional //事务
    public void saveSpuInfo(SpuInfo spuInfo) {
        //1:保存SPU数据 spu_info
        spuInfoMapper.insert(spuInfo);
        //2:保存SPU图片
        List<SpuImage> spuImageList = spuInfo.getSpuImageList();
        if (null != spuImageList && spuImageList.size() > 0) {
            spuImageList.forEach(i -> {
                //设置SPU的主键作为外键
                i.setSpuId(spuInfo.getId());
                spuImageMapper.insert(i);
            });
        }
        //3:销售属性集合
        List<SpuSaleAttr> spuSaleAttrList = spuInfo.getSpuSaleAttrList();
        if (null != spuSaleAttrList && spuSaleAttrList.size() > 0) {
            spuSaleAttrList.forEach(s -> {
                //外键
                s.setSpuId(spuInfo.getId());
                //保存销售属性
                spuSaleAttrMapper.insert(s);
                //4:销售属性值集合
                List<SpuSaleAttrValue> spuSaleAttrValueList = s.getSpuSaleAttrValueList();
                spuSaleAttrValueList.forEach(v -> {
                    //外键
                    v.setSpuId(spuInfo.getId());
                    //销售属性名称(冗余）
                    v.setSaleAttrName(s.getSaleAttrName());
                    spuSaleAttrValueMapper.insert(v);
                });

            });
        }
    }

    //获取SKU分页列表
    @Override
    public IPage<SkuInfo> list(IPage<SkuInfo> skuInfoIPage) {
        return skuInfoMapper.selectPage(skuInfoIPage, null);
    }

    //根据SPuID查询此商品的所有图片
    @Override
    public List<SpuImage> spuImageList(Long spuId) {
        return spuImageMapper.selectList(new QueryWrapper<SpuImage>().eq("spu_id", spuId));
    }

    //根据SPUID查询所有此商品销售属性及属性值
    @Override
    public List<SpuSaleAttr> spuSaleAttrList(Long spuId) {
        //1:SPU销售属性表 SPU销售属性值表


        return spuSaleAttrMapper.spuSaleAttrList(spuId);
    }


    //保存SKU
    @Override
    public void saveSkuInfo(SkuInfo skuInfo) {
        //1:sku_info
        skuInfoMapper.insert(skuInfo);//返回值为skuInfo 主键
        //2:sku_image
        List<SkuImage> skuImageList = skuInfo.getSkuImageList();
        skuImageList.forEach(image -> {
            //外键
            image.setSkuId(skuInfo.getId());
            skuImageMapper.insert(image);
        });
        //3:sku_attr_value 平台属性及值表
        List<SkuAttrValue> skuAttrValueList = skuInfo.getSkuAttrValueList();
        skuAttrValueList.forEach(sav -> {
            //外键
            sav.setSkuId(skuInfo.getId());
            skuAttrValueMapper.insert(sav);
        });
        //4:sku_sale_attr_value 销售属性及值表
        List<SkuSaleAttrValue> skuSaleAttrValueList = skuInfo.getSkuSaleAttrValueList();
        skuSaleAttrValueList.forEach(ssav -> {
            //商品表的主键
            ssav.setSpuId(skuInfo.getSpuId());
            //库存表的主键
            ssav.setSkuId(skuInfo.getId());
            skuSaleAttrValueMapper.insert(ssav);
        });
        //保存一份到索引库 耦合度太强了  远程调用搜索微服务

    }


    @Autowired
    private RabbitService rabbitService;

    //上架
    @Override
    public void onSale(Long skuId) {
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setId(skuId);
        skuInfo.setIsSale(1);//1:表示 上架
        skuInfoMapper.updateById(skuInfo);
        //发送消息
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_GOODS
        ,MqConst.ROUTING_GOODS_UPPER,skuId.toString());

    }

    //下架
    @Override
    public void cancelSale(Long skuId) {
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setId(skuId);
        skuInfo.setIsSale(0);//0:表示 上架
        skuInfoMapper.updateById(skuInfo);
        //发送消息
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_GOODS
                ,MqConst.ROUTING_GOODS_LOWER,skuId.toString());
    }


    @Autowired
    private RedissonClient redissonClient;

    //根据SkuID查询SkuInfo 及图片
    public SkuInfo getSkuInfo(Long skuId) {//当前方法业务需要来解决 到底是设置多久的过期时间是正确
        //1: 防止击穿   一个热点数据  有一百万的请求  只希望第一个请求上锁 去DB查询数据 保存一份到Redis中
        //2: 后续百万-1个请求还有没有必须上锁  答案没必上锁
        //什么时候上锁 ？ 缓存是正确没有数据  请求还是百万级的请求 最应该上锁
        //1) 先判断缓存中是否有数据  有数据就没有必须上锁
        //2) 判断缓存中 发现没有数据 必须上锁

        //1:先获取缓存中是否有SkuInfo
        String key = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(key);
        //2:有 直接返回SkuInfo
        if (null != skuInfo) {
            return skuInfo;
        }
        //上锁
        RLock lock = redissonClient.getLock("getSkuInfo:" + skuId + ":lock");
        //参数1：尝试多久 过了时间 就放弃    尝试时间 == 锁的持有时间    第一线程 获取到锁 持有时间1秒 第N线程
        try {
            boolean res = lock.tryLock(RedisConst.SKULOCK_EXPIRE_PX1,
                    RedisConst.SKULOCK_EXPIRE_PX2, TimeUnit.SECONDS);//手动解锁已经不可能
            if (res) {
                //在1秒中获取锁

                //3:没有 查询Mysql数据库
                skuInfo = getSkuInfoDB(skuId);
                //判断  没有  穿透  无此数据
                if (null == skuInfo) {
                    //这就是空值  null  穿透发生时 SkuInfo就是空对象（属性没有值）
                    SkuInfo skuInfo1 = new SkuInfo();
                    redisTemplate.opsForValue().set(key, skuInfo1, 5, TimeUnit.MINUTES);
                    return skuInfo1;
                }
//        Random random = new Random();
//        int i = random.nextInt(5);
                //4:保存缓存一份   参数3：超时时间   参数4：单位
                //redisTemplate.opsForValue().set(key,skuInfo,RedisConst.SKUKEY_TIMEOUT + i*60, TimeUnit.SECONDS);
                redisTemplate.opsForValue().set(key, skuInfo, RedisConst.SKUKEY_TIMEOUT, TimeUnit.SECONDS);
                //5:返回SkuInfo
                return skuInfo;
            } else {
                //未获取锁  直接获取缓存数据 第一线程已经查完DB并保存缓存的数据保存了一份
                return (SkuInfo) redisTemplate.opsForValue().get(key);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock.isLocked()) {//判断是否有锁  有锁就解除  没有锁就不解
                lock.unlock();//只有我自己访问我的电脑  一个请求  没锁
            }
        }
        //如果上面抛了异常 查询DB数据库  程序出现问题 临时使用数据库顶一顶
        return getSkuInfoDB(skuId);
    }

    //从Mysql数据库查询
    public SkuInfo getSkuInfoDB(Long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        //健壮的程序
        if (null != skuInfo) {
            //查询图片
            List<SkuImage> skuImageList = skuImageMapper.selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuId));
            skuInfo.setSkuImageList(skuImageList);
        }
        return skuInfo;
    }


    //根据三级分类ID查询分类视图
    @Override
    @GmallCache(prefix = "baseCategoryViewByCategory3Id:")
    public BaseCategoryView getCategoryView(Long category3_id) {
        return baseCategoryViewMapper.selectById(category3_id);
    }

    // 查询销售属性 及属性值 及 选中当前商品
    @Override
    @GmallCache(prefix = "spuSale:")
    public List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(Long skuId, Long spuId) {
        return spuSaleAttrMapper.getSpuSaleAttrListCheckBySku(skuId, spuId);
    }

    //点击其他销售属性值的组合，跳转到另外的sku页面
    //{“1|5|7”:”1”,“1|4|7”:”2”,.....}
    @Override
    @GmallCache(prefix = "getSkuValueIdsMap:")
    public Map getSkuValueIdsMap(Long spuId) {
        List<Map> listMap = skuSaleAttrValueMapper.getSkuValueIdsMap(spuId);
        // Map1 sku_id  1   value_ids 1|5|7
        // Map2 sku_id  2   value_ids 1|4|7
        Map map = new HashMap();
        listMap.forEach(m -> {
            map.put(m.get("value_ids"), m.get("sku_id"));
        });
        return map;
    }

    //单独获取库存的价格
    @Override
    public BigDecimal getSkuPrice(Long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        if (null != skuInfo) {
            return skuInfo.getPrice();
        } else {
            return null;
        }
    }

    //为了首页加载一二三级商品分类
    @Override
    public List<JSONObject> getBaseCategoryList() {

        //最终返回结果
        List<JSONObject> result = new ArrayList<>();
        //1.一二三级分类的视图   所有结果
        List<BaseCategoryView> baseCategoryViewList = baseCategoryViewMapper.selectList(null);
        //对上面的集合按照一级分类ID进行分组  之后返回集合
        //返回值Map  K：一级分类的ID  V：集合
        // Map1   K : 1 V 图片、音像、对应的集合 60长度
        // Map2   K : 2 V 手机对应的集合 24长度
        // Map3   K : 3 V 家用电器 对应的集合 24长度
        Map<Long, List<BaseCategoryView>> category1IdList = baseCategoryViewList.stream()
                .collect(Collectors.groupingBy(BaseCategoryView::getCategory1Id));
        //2.一级分类组装数据结构
        // 1) 角标 从1开始
        int index = 1;
        for (Map.Entry<Long, List<BaseCategoryView>> category1IdEntry : category1IdList.entrySet()) {
            JSONObject category1IdJsonObject = new JSONObject();
            category1IdJsonObject.put("index", index++);
            //2) 一级分类ID
            category1IdJsonObject.put("categoryId", category1IdEntry.getKey());
            //3)一级分类的名称
            category1IdJsonObject.put("categoryName", category1IdEntry.getValue().get(0).getCategory1Name());

            //准备二级分类的集合
            //Map1 K 二级分类的ID 13 V：集合 2长度
            //Map2 K 二级分类的ID 14 V：集合 4长度
            Map<Long, List<BaseCategoryView>> category2IdList = category1IdEntry.getValue()
                    .stream().collect(Collectors.groupingBy(BaseCategoryView::getCategory2Id));
            //二级分类集合
            List<JSONObject> category2IdJSONObject = new ArrayList<>();
            for (Map.Entry<Long, List<BaseCategoryView>> category2IdEntry : category2IdList.entrySet()) {
                JSONObject category2IdJsonObject = new JSONObject();
                //1)二级分类的ID
                category2IdJsonObject.put("categoryId", category2IdEntry.getKey());
                //2)二级分类的名称
                category2IdJsonObject.put("categoryName", category2IdEntry.getValue().get(0).getCategory2Name());
                //准备三级分类的集合
                List<JSONObject> category3List = category2IdEntry.getValue().stream().map(category3 -> {
                    JSONObject category3IdJsonObject = new JSONObject();
                    category3IdJsonObject.put("categoryId", category3.getCategory3Id());
                    category3IdJsonObject.put("categoryName", category3.getCategory3Name());
                    return category3IdJsonObject;
                }).collect(Collectors.toList());
                //3)二级分类的孩子(三级分类集合）
                category2IdJsonObject.put("categoryChild",category3List);
                //追加每一个二级分类对象
                category2IdJSONObject.add(category2IdJsonObject);
            }
            //4) 一级分类的所有孩子 （二级分类的集合） categoryChild
            category1IdJsonObject.put("categoryChild", category2IdJSONObject);
            //追加一个一级分类对象
            result.add(category1IdJsonObject);
        }


        return result;
    }
    //Sku相关的平台属性及属性值 集合
    @Override
    public List<SkuAttrValue> getAttrList(Long skuId) {
        //sku_attr_value  sku相关联的平台属性及属性值表
        //base_attr_info
        //base_attr_value

        return skuAttrValueMapper.getAttrList(skuId);
    }
    //Sku相关的品牌信息
    @Override
    public BaseTrademark getTrademark(Long tmId) {
        return baseTrademarkMapper.selectById(tmId);
    }

    @Autowired
    private SkuAttrValueMapper skuAttrValueMapper;
    @Autowired
    private SkuSaleAttrValueMapper skuSaleAttrValueMapper;
    @Autowired
    private BaseCategoryViewMapper baseCategoryViewMapper;

}
